package models;

import java.util.HashMap;
import java.util.Map;

import models.msgs.AfterBetting;
import models.msgs.AllIn;
import models.msgs.Bet;
import models.msgs.BuyIn;
import models.msgs.Call;
import models.msgs.Check;
import models.msgs.CollectBuyIn;
import models.msgs.Cash;
import models.msgs.FirstBetting;
import models.msgs.Fold;
import models.msgs.Info;
import models.msgs.LastBet;
import models.msgs.OptionsOfBetting;
import models.msgs.Pot;
import models.msgs.Quit;
import models.msgs.Raise;
import akka.actor.ActorRef;
import akka.actor.UntypedActor;

public class Croupier extends UntypedActor {
	private int pula;
	private int cashForOnePlayer;
	private int buyIn;
	private Map<String, ActorRef> playersInThisGame;
	private String[] nameOfPlayer;
	private int firstPlayer;
	private String name;
	private ActorRef table;
	private Map<String,Integer> betOfPlayer;
	
	private int numberOfraises;
	private int numberOfcalls;
	private int currentPlayer;
	private OptionsOfBetting currentOptions;
	private int numerOfallin;
	
	public Croupier(int _buyIn, int _cashForOnePlayer, Map<String, ActorRef> _playersInThisGame,ActorRef _table, int _firstPlayer) {
		pula = 0;
		buyIn = _buyIn;
		cashForOnePlayer = _cashForOnePlayer;
		playersInThisGame = new HashMap<String,ActorRef>(); 
		playersInThisGame.putAll(_playersInThisGame);
		nameOfPlayer = playersInThisGame.keySet().toArray(new String[playersInThisGame.size()]);
		firstPlayer = _firstPlayer;	
        table = _table;
		name = "God of Money";
        
		
		betOfPlayer = new HashMap<String, Integer>();
		for (int i = 0; i < nameOfPlayer.length; i++) {
			betOfPlayer.put(nameOfPlayer[i], 0);
		}
		
		numberOfraises = 0;
		numberOfcalls = 0;
		numerOfallin =0;
		currentPlayer = firstPlayer;
		currentOptions = new OptionsOfBetting(true, true, false, false, true, true, 0);
	}
	
	public void giveCash() {
		notifyAllPlayers(new Cash(cashForOnePlayer));
	}
	public void collectBuyIn() {
		notifyAllPlayers(new CollectBuyIn(buyIn));
		notifyAllPlayers(new Info("I collected the buy-in",name));
	}
	@Override
	public void onReceive(Object msg) throws Exception {
		if (msg instanceof Cash) {
			giveCash();
		} if (msg instanceof CollectBuyIn) {
			collectBuyIn();
		} if (msg instanceof BuyIn) { 
			BuyIn buyIn = (BuyIn)msg;
			
			onBuyIn(buyIn);
			
			notifyAllPlayers(new Pot(pula));
		} else if (msg instanceof FirstBetting) { 
			notifyNextPlayer();
			
		} else if (msg instanceof Bet) {  ////////////////bet
			Bet bet = (Bet)msg;
			
			onBet(bet);
			
			if((numberOfcalls == playersInThisGame.size() && numberOfraises ==1) || (numberOfcalls==0 && numberOfraises==0) ){
				endOfBet();
			} else {
				notifyNextPlayer();
			}
			
		} else if (msg instanceof Check) {  ////////////////check	
			
			onCheck();
			
			if((numberOfcalls == playersInThisGame.size() && numberOfraises ==1) || (numberOfcalls==0 && numberOfraises==0) ){
				endOfBet();
			} else {
				notifyNextPlayer();
			}
		} else if (msg instanceof Raise) {  ////////////////raise
			Raise raise = (Raise)msg;
			
			OnRaise(raise);
			
			notifyNextPlayer();
			
		} else if (msg instanceof Call) {  ////////////////call
			Call call = (Call)msg;
			
			onCall(call);
			
			if((numberOfcalls == playersInThisGame.size() && numberOfraises ==1) || (numberOfcalls==0 && numberOfraises==0) ){
				endOfBet();
			} else {
				notifyNextPlayer();
			}
		} else if (msg instanceof Fold) {  ////////////////fold
			String _name = ((Fold)msg).getName();
			
			onFold(_name);
			
			if((numberOfcalls == playersInThisGame.size() && numberOfraises == 1) || (numerOfallin==playersInThisGame.size() ) ){
				endOfBet();
			} else if(playersInThisGame.size()>1){
				notifyNextPlayer();
			} else {
				endOfBet();
			}
		} else if (msg instanceof AllIn) {  ////////////////all-in
			AllIn allin = (AllIn)msg;
			
			onAllin(allin);
			
			if( numerOfallin == playersInThisGame.size() ){
				endOfBet();
			} else {

				playersInThisGame.get(nameOfPlayer[currentPlayer%playersInThisGame.size()]).tell(currentOptions, getSelf());
			}
		} if(msg instanceof Quit)  {            
			Quit quit = (Quit)msg;
            playersInThisGame.remove(quit.getName());
    		playersInThisGame.remove(quit.getName());
    		betOfPlayer.remove(quit.getName());
    		nameOfPlayer = playersInThisGame.keySet().toArray(new String[playersInThisGame.size()]);
        } 
		else {
			unhandled(msg);
		}
		
	}

	public void onFold(String _name) {
		playersInThisGame.remove(_name);
		betOfPlayer.remove(_name);
		nameOfPlayer = playersInThisGame.keySet().toArray(new String[playersInThisGame.size()]);
	}

	public void onAllin(AllIn allin) {
		numerOfallin++;
		numberOfraises=1;
		pula += allin.getHeightOfBet();
		notifyAllPlayers(new Pot(pula));
		notifyAllPlayers(new LastBet(allin.getHeightOfBet()));
		int amoutOfBet = betOfPlayer.get(allin.getName());
		amoutOfBet += allin.getHeightOfBet();
		betOfPlayer.put(allin.getName(), amoutOfBet);
		currentPlayer = (currentPlayer+1)%playersInThisGame.size();
		currentOptions.setBet(false);
		currentOptions.setCheck(false);
		currentOptions.setCall(false);
		currentOptions.setRaise(false);
		currentOptions.setLastBet(allin.getHeightOfBet());
	}

	public void OnRaise(Raise raise) {
		numberOfraises = 1;
		numberOfcalls = -1;
		pula += raise.getHeightOfBet();
		notifyAllPlayers(new Pot(pula));
		notifyAllPlayers(new LastBet(raise.getHeightOfBet()));  
		int amoutOfBet = betOfPlayer.get(raise.getName());
		amoutOfBet += raise.getHeightOfBet();
		betOfPlayer.put(raise.getName(), amoutOfBet);
		currentOptions.setLastBet(raise.getHeightOfBet());
		currentPlayer = (currentPlayer+1)%playersInThisGame.size();
	}

	public void onCheck() {
		numberOfcalls++;
		currentPlayer = (currentPlayer+1)%playersInThisGame.size();
	}

	public void onBet(Bet bet) {
		numberOfraises = 1;
		numberOfcalls = -1;
		pula += bet.getHeightOfBet();
		int amoutOfBet = betOfPlayer.get(bet.getName());
		amoutOfBet += bet.getHeightOfBet();
		betOfPlayer.put(bet.getName(), amoutOfBet);
		currentPlayer = (currentPlayer+1)%playersInThisGame.size();
		currentOptions.setLastBet(bet.getHeightOfBet());
		currentOptions.setBet(false);
		currentOptions.setCheck(false);
		currentOptions.setCall(true);
		currentOptions.setRaise(true);
		notifyAllPlayers(new LastBet(bet.getHeightOfBet()));
		notifyAllPlayers(new Pot(pula));
	}

	public void onCall(Call call) {
		numberOfcalls++;
		pula += call.getHeightOfBet();
		notifyAllPlayers(new Pot(pula));
		int amoutOfBet = betOfPlayer.get(call.getName());
		amoutOfBet += call.getHeightOfBet();
		betOfPlayer.put(call.getName(), amoutOfBet);
		currentOptions.setLastBet(call.getHeightOfBet());
		currentPlayer = (currentPlayer+1)%playersInThisGame.size();
	}

	private void endOfBet() {
		table.tell(new AfterBetting(pula,(numerOfallin >0 ? true:false), playersInThisGame, betOfPlayer ), getSelf());
	}

	private void notifyNextPlayer() {
		playersInThisGame.get(nameOfPlayer[currentPlayer%playersInThisGame.size()]).tell(currentOptions, getSelf());
	}

	public void onBuyIn(BuyIn buyIn) {
		betOfPlayer.put(buyIn.getName(), buyIn.getBuyIn());
		pula+=buyIn.getBuyIn();
	}
    public void notifyAllPlayers(Object msg) {
        for (ActorRef player : playersInThisGame.values()) {
            player.tell(msg, getSelf());
        }
    }   
    public OptionsOfBetting getOptions() {
    	return currentOptions;
    }
}